﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using EDOSwit.Entity;
using FastFood.Business.Entities;
using FastFood.Business.Entities.Tipos;
using Microsoft.Practices.EnterpriseLibrary.Data;

namespace FastFood.DataAccess
{
    public class TrasladoData : Singleton<TrasladoData>
    {
        #region Attributes

        private readonly Database BaseDatos = DatabaseFactory.CreateDatabase();

        #endregion

        #region Methods

        /// <summary>
        /// Selects all records from the Traslado table by a foreign key.
        /// </summary>
        public IList<Traslado> Listar()
        {
            var entidades = new List<Traslado>();
            var comando = BaseDatos.GetStoredProcCommand("SelectAllTraslado");

            using (var lector = BaseDatos.ExecuteReader(comando))
            {
                while (lector.Read())
                {
                    entidades.Add(new Traslado
                    {
                        trasladoID = lector.IsDBNull(lector.GetOrdinal("trasladoID")) ? default(int) : lector.GetInt32(lector.GetOrdinal("trasladoID")),
                        loteID = lector.IsDBNull(lector.GetOrdinal("loteID")) ? default(int) : lector.GetInt32(lector.GetOrdinal("loteID")),
                        origenAlmacenID = lector.IsDBNull(lector.GetOrdinal("origenAlmacenID")) ? default(short) : lector.GetInt16(lector.GetOrdinal("origenAlmacenID")),
                        destinoAlmacenID = lector.IsDBNull(lector.GetOrdinal("destinoAlmacenID")) ? default(short) : lector.GetInt16(lector.GetOrdinal("destinoAlmacenID")),
                        cantidad = MathExpression.Round(Convert.ToSingle(lector.GetValue(lector.GetOrdinal("cantidad")))),
                        fecha = lector.IsDBNull(lector.GetOrdinal("fecha")) ? default(DateTime) : lector.GetDateTime(lector.GetOrdinal("fecha")),
                        personalID = lector.IsDBNull(lector.GetOrdinal("personalID")) ? default(short) : lector.GetInt16(lector.GetOrdinal("personalID")),
                        insumoID = lector.IsDBNull(lector.GetOrdinal("insumoID")) ? default(short) : lector.GetInt16(lector.GetOrdinal("insumoID")),
                        nombreInsumo = lector.IsDBNull(lector.GetOrdinal("insumo")) ? default(string) : lector.GetString(lector.GetOrdinal("insumo")),
                        numeroLote = lector.IsDBNull(lector.GetOrdinal("lote")) ? default(string) : lector.GetString(lector.GetOrdinal("lote")),
                        nombreAlmacenOrigen = lector.IsDBNull(lector.GetOrdinal("AlmacenOrigen")) ? default(string) : lector.GetString(lector.GetOrdinal("AlmacenOrigen")),
                        nombreAlmacenDestino = lector.IsDBNull(lector.GetOrdinal("AlmacenDestino")) ? default(string) : lector.GetString(lector.GetOrdinal("AlmacenDestino"))
                    });
                }
            }
            comando.Dispose();
            return entidades;
        }

        /// <summary>
        /// Selects all records from the Traslado table by a foreign key.
        /// </summary>
        public Traslado Obtener(int trasladoID)
        {
            var traslado = default(Traslado);
            var comando = BaseDatos.GetStoredProcCommand("SelectTraslado");

            BaseDatos.AddInParameter(comando, "trasladoID", DbType.Int32, trasladoID);

            using (var lector = BaseDatos.ExecuteReader(comando))
            {
                while (lector.Read())
                {
                    traslado = new Traslado
                    {
                        trasladoID = lector.IsDBNull(lector.GetOrdinal("trasladoID")) ? default(int) : lector.GetInt32(lector.GetOrdinal("trasladoID")),
                        loteID = lector.IsDBNull(lector.GetOrdinal("loteID")) ? default(int) : lector.GetInt32(lector.GetOrdinal("loteID")),
                        origenAlmacenID = lector.IsDBNull(lector.GetOrdinal("origenAlmacenID")) ? default(short) : lector.GetInt16(lector.GetOrdinal("origenAlmacenID")),
                        destinoAlmacenID = lector.IsDBNull(lector.GetOrdinal("destinoAlmacenID")) ? default(short) : lector.GetInt16(lector.GetOrdinal("destinoAlmacenID")),
                        cantidad = MathExpression.Round(Convert.ToSingle(lector.GetValue(lector.GetOrdinal("cantidad")))),
                        fecha = lector.IsDBNull(lector.GetOrdinal("fecha")) ? default(DateTime) : lector.GetDateTime(lector.GetOrdinal("fecha")),
                        personalID = lector.IsDBNull(lector.GetOrdinal("personalID")) ? default(short) : lector.GetInt16(lector.GetOrdinal("personalID"))
                    };
                }
            }
            comando.Dispose();
            return traslado;
        }

        /// <summary>
        /// Saves a record to the Traslado table.
        /// </summary>
        public int Agregar(Traslado traslado)
        {
            var trasladoID = 0;
            using (DbConnection conexion = BaseDatos.CreateConnection())
            {
                conexion.Open();
                DbTransaction transaccion = conexion.BeginTransaction();

                try
                {
                    traslado.cantidad = MathExpression.Round(traslado.cantidad);

                    var comando = BaseDatos.GetStoredProcCommand("InsertTraslado");
                    BaseDatos.AddOutParameter(comando, "trasladoID", DbType.Int32, 4);
                    BaseDatos.AddInParameter(comando, "loteID", DbType.Int32, traslado.loteID);
                    BaseDatos.AddInParameter(comando, "origenAlmacenID", DbType.Int16, traslado.origenAlmacenID);
                    BaseDatos.AddInParameter(comando, "destinoAlmacenID", DbType.Int16, traslado.destinoAlmacenID);
                    BaseDatos.AddInParameter(comando, "cantidad", DbType.Single, traslado.cantidad);
                    BaseDatos.AddInParameter(comando, "fecha", DbType.DateTime, traslado.fecha);
                    BaseDatos.AddInParameter(comando, "personalID", DbType.Int16, traslado.personalID);

                    var resultado = BaseDatos.ExecuteNonQuery(comando, transaccion);
                    if (resultado == 0) throw new Exception("Error al Agregar Traslado.");

                    trasladoID = (int)BaseDatos.GetParameterValue(comando, "trasladoID");

                    double stock = 0;
                    var comandoAlmacenamiento = BaseDatos.GetStoredProcCommand("SelectAlmacenamientos");
                    BaseDatos.AddInParameter(comandoAlmacenamiento, "loteID", DbType.Int32, traslado.loteID);
                    BaseDatos.AddInParameter(comandoAlmacenamiento, "almacenID", DbType.Int16, traslado.origenAlmacenID);

                    using (var lector = BaseDatos.ExecuteReader(comandoAlmacenamiento, transaccion))
                    {
                        if (lector.Read())
                        {
                            stock = MathExpression.Round(lector.GetDouble(lector.GetOrdinal("stock")));
                        }
                    }

                    var movimiento = new Movimientos
                                {
                                    loteID = traslado.loteID,
                                    almacenID = traslado.origenAlmacenID,
                                    cantidad = -1 * traslado.cantidad,
                                    fecha = traslado.fecha,
                                    motivo = (byte)MotivoMovimiento.Traslados,
                                    operacionID = trasladoID,
                                    personalID = traslado.personalID,
                                    saldo = stock - traslado.cantidad
                                };

                    AlmacenamientosData.Instancia.ModificarStock(movimiento.loteID, movimiento.almacenID, movimiento.cantidad, transaccion);
                    LotesData.Instancia.ModificarStock(movimiento.loteID, movimiento.cantidad, transaccion);
                    InsumosData.Instancia.ModificarStock(movimiento.insumoID, movimiento.cantidad, transaccion);
                    MovimientosData.Instancia.AgregarConFecha(movimiento, transaccion);

                    comandoAlmacenamiento = BaseDatos.GetStoredProcCommand("SelectAlmacenamientos");
                    BaseDatos.AddInParameter(comandoAlmacenamiento, "loteID", DbType.Int32, traslado.loteID);
                    BaseDatos.AddInParameter(comandoAlmacenamiento, "almacenID", DbType.Int16, traslado.destinoAlmacenID);

                    using (var lector = BaseDatos.ExecuteReader(comandoAlmacenamiento, transaccion))
                    {
                        if (lector.Read())
                        {
                            stock = lector.GetDouble(lector.GetOrdinal("stock"));
                        }
                    }

                    movimiento.cantidad = traslado.cantidad;
                    movimiento.saldo = stock + traslado.cantidad;
                    movimiento.almacenID = traslado.destinoAlmacenID;

                    AlmacenamientosData.Instancia.ModificarStock(movimiento.loteID, movimiento.almacenID, movimiento.cantidad, transaccion);
                    LotesData.Instancia.ModificarStock(movimiento.loteID, movimiento.cantidad, transaccion);
                    InsumosData.Instancia.ModificarStock(movimiento.insumoID, movimiento.cantidad, transaccion);
                    MovimientosData.Instancia.AgregarConFecha(movimiento, transaccion);

                    transaccion.Commit();
                }
                catch (Exception ex)
                {
                    transaccion.Rollback();
                    throw new Exception(ex.Message);
                }
                conexion.Close();
            }
            return trasladoID;
        }

        /// <summary>
        /// Updates a record in the Traslado table.
        /// </summary>
        public bool Modificar(Traslado traslado)
        {
            using (DbConnection conexion = BaseDatos.CreateConnection())
            {
                conexion.Open();
                DbTransaction transaccion = conexion.BeginTransaction();

                try
                {
                    #region Retornar el traslado realizado anteriormente

                    var trasladoAntiguo = Obtener(traslado.trasladoID);
                    trasladoAntiguo.cantidad = MathExpression.Round(trasladoAntiguo.cantidad); 

                    double stock = 0;
                    var comandoAlmacenamiento = BaseDatos.GetStoredProcCommand("SelectAlmacenamientos");
                    BaseDatos.AddInParameter(comandoAlmacenamiento, "loteID", DbType.Int32, trasladoAntiguo.loteID);
                    BaseDatos.AddInParameter(comandoAlmacenamiento, "almacenID", DbType.Int16, trasladoAntiguo.origenAlmacenID);

                    using (var lector = BaseDatos.ExecuteReader(comandoAlmacenamiento, transaccion))
                    {
                        if (lector.Read())
                        {
                            stock = MathExpression.Round(lector.GetDouble(lector.GetOrdinal("stock")));
                        }
                    }

                    var movimiento = new Movimientos
                    {
                        loteID = trasladoAntiguo.loteID,
                        almacenID = trasladoAntiguo.origenAlmacenID,
                        cantidad = trasladoAntiguo.cantidad,
                        fecha = traslado.fecha,
                        motivo = (byte)MotivoMovimiento.Traslados,
                        operacionID = traslado.trasladoID,
                        personalID = traslado.personalID,
                        saldo = stock - trasladoAntiguo.cantidad,
                        insumoID = traslado.insumoID
                    };

                    AlmacenamientosData.Instancia.ModificarStock(movimiento.loteID, movimiento.almacenID, movimiento.cantidad, transaccion);
                    LotesData.Instancia.ModificarStock(movimiento.loteID, movimiento.cantidad, transaccion);
                    InsumosData.Instancia.ModificarStock(movimiento.insumoID, movimiento.cantidad, transaccion);
                    MovimientosData.Instancia.AgregarConFecha(movimiento, transaccion);

                    comandoAlmacenamiento = BaseDatos.GetStoredProcCommand("SelectAlmacenamientos");
                    BaseDatos.AddInParameter(comandoAlmacenamiento, "loteID", DbType.Int32, trasladoAntiguo.loteID);
                    BaseDatos.AddInParameter(comandoAlmacenamiento, "almacenID", DbType.Int16, trasladoAntiguo.destinoAlmacenID);

                    using (var lector = BaseDatos.ExecuteReader(comandoAlmacenamiento, transaccion))
                    {
                        if (lector.Read())
                        {
                            stock = lector.GetDouble(lector.GetOrdinal("stock"));
                        }
                    }

                    movimiento.cantidad = -1 * trasladoAntiguo.cantidad;
                    movimiento.saldo = stock + trasladoAntiguo.cantidad;
                    movimiento.almacenID = trasladoAntiguo.destinoAlmacenID;

                    AlmacenamientosData.Instancia.ModificarStock(movimiento.loteID, movimiento.almacenID, movimiento.cantidad, transaccion);
                    LotesData.Instancia.ModificarStock(movimiento.loteID, movimiento.cantidad, transaccion);
                    InsumosData.Instancia.ModificarStock(movimiento.insumoID, movimiento.cantidad, transaccion);
                    MovimientosData.Instancia.AgregarConFecha(movimiento, transaccion);

                    #endregion

                    #region Registrar el nuevo traslado realizado

                    traslado.cantidad = MathExpression.Round(traslado.cantidad);

                    var comando = BaseDatos.GetStoredProcCommand("UpdateTraslado");
                    BaseDatos.AddInParameter(comando, "trasladoID", DbType.Int32, traslado.trasladoID);
                    BaseDatos.AddInParameter(comando, "loteID", DbType.Int32, traslado.loteID);
                    BaseDatos.AddInParameter(comando, "origenAlmacenID", DbType.Int16, traslado.origenAlmacenID);
                    BaseDatos.AddInParameter(comando, "destinoAlmacenID", DbType.Int16, traslado.destinoAlmacenID);
                    BaseDatos.AddInParameter(comando, "cantidad", DbType.Single, traslado.cantidad);
                    BaseDatos.AddInParameter(comando, "fecha", DbType.DateTime, traslado.fecha);
                    BaseDatos.AddInParameter(comando, "personalID", DbType.Int16, traslado.personalID);

                    var resultado = BaseDatos.ExecuteNonQuery(comando, transaccion);
                    if (resultado == 0) throw new Exception("Error al Modificar Traslado.");

                    stock = 0;
                    comandoAlmacenamiento = BaseDatos.GetStoredProcCommand("SelectAlmacenamientos");
                    BaseDatos.AddInParameter(comandoAlmacenamiento, "loteID", DbType.Int32, traslado.loteID);
                    BaseDatos.AddInParameter(comandoAlmacenamiento, "almacenID", DbType.Int16, traslado.origenAlmacenID);

                    using (var lector = BaseDatos.ExecuteReader(comandoAlmacenamiento, transaccion))
                    {
                        if (lector.Read())
                        {
                            stock = MathExpression.Round(lector.GetDouble(lector.GetOrdinal("stock")));
                        }
                    }

                    movimiento = new Movimientos
                    {
                        loteID = traslado.loteID,
                        almacenID = traslado.origenAlmacenID,
                        cantidad = -1 * traslado.cantidad,
                        fecha = traslado.fecha,
                        motivo = (byte)MotivoMovimiento.Traslados,
                        operacionID = traslado.trasladoID,
                        personalID = traslado.personalID,
                        saldo = stock - traslado.cantidad
                    };

                    AlmacenamientosData.Instancia.ModificarStock(movimiento.loteID, movimiento.almacenID, movimiento.cantidad, transaccion);
                    LotesData.Instancia.ModificarStock(movimiento.loteID, movimiento.cantidad, transaccion);
                    InsumosData.Instancia.ModificarStock(movimiento.insumoID, movimiento.cantidad, transaccion);
                    MovimientosData.Instancia.AgregarConFecha(movimiento, transaccion);

                    comandoAlmacenamiento = BaseDatos.GetStoredProcCommand("SelectAlmacenamientos");
                    BaseDatos.AddInParameter(comandoAlmacenamiento, "loteID", DbType.Int32, traslado.loteID);
                    BaseDatos.AddInParameter(comandoAlmacenamiento, "almacenID", DbType.Int16, traslado.destinoAlmacenID);

                    using (var lector = BaseDatos.ExecuteReader(comandoAlmacenamiento, transaccion))
                    {
                        if (lector.Read())
                        {
                            stock = MathExpression.Round(lector.GetDouble(lector.GetOrdinal("stock")));
                        }
                    }

                    movimiento.cantidad = traslado.cantidad;
                    movimiento.saldo = stock + traslado.cantidad;
                    movimiento.almacenID = traslado.destinoAlmacenID;

                    AlmacenamientosData.Instancia.ModificarStock(movimiento.loteID, movimiento.almacenID, movimiento.cantidad, transaccion);
                    LotesData.Instancia.ModificarStock(movimiento.loteID, movimiento.cantidad, transaccion);
                    InsumosData.Instancia.ModificarStock(movimiento.insumoID, movimiento.cantidad, transaccion);
                    MovimientosData.Instancia.AgregarConFecha(movimiento, transaccion);

                    #endregion

                    transaccion.Commit();
                }
                catch (Exception ex)
                {
                    transaccion.Rollback();
                    throw new Exception(ex.Message);
                }
                conexion.Close();
            }
            return true;
        }

        /// <summary>
        /// Deletes a record from the Traslado table by its primary key.
        /// </summary>
        public bool Eliminar(Traslado traslado)
        {
            using (DbConnection conexion = BaseDatos.CreateConnection())
            {
                conexion.Open();
                DbTransaction transaccion = conexion.BeginTransaction();

                try
                {
                    var trasladoAntiguo = Obtener(traslado.trasladoID);
                    trasladoAntiguo.cantidad = MathExpression.Round(trasladoAntiguo.cantidad);

                    double stock = 0;
                    var comandoAlmacenamiento = BaseDatos.GetStoredProcCommand("SelectAlmacenamientos");
                    BaseDatos.AddInParameter(comandoAlmacenamiento, "loteID", DbType.Int32, trasladoAntiguo.loteID);
                    BaseDatos.AddInParameter(comandoAlmacenamiento, "almacenID", DbType.Int16, trasladoAntiguo.origenAlmacenID);

                    using (var lector = BaseDatos.ExecuteReader(comandoAlmacenamiento, transaccion))
                    {
                        if (lector.Read())
                        {
                            stock = lector.GetDouble(lector.GetOrdinal("stock"));
                        }
                    }

                    var movimiento = new Movimientos
                    {
                        loteID = trasladoAntiguo.loteID,
                        almacenID = trasladoAntiguo.origenAlmacenID,
                        cantidad = trasladoAntiguo.cantidad,
                        fecha = traslado.fecha,
                        motivo = (byte)MotivoMovimiento.Traslados,
                        operacionID = traslado.trasladoID,
                        personalID = traslado.personalID,
                        saldo = stock - trasladoAntiguo.cantidad,
                        insumoID = traslado.insumoID
                    };

                    AlmacenamientosData.Instancia.ModificarStock(movimiento.loteID, movimiento.almacenID, movimiento.cantidad, transaccion);
                    LotesData.Instancia.ModificarStock(movimiento.loteID, movimiento.cantidad, transaccion);
                    InsumosData.Instancia.ModificarStock(movimiento.insumoID, movimiento.cantidad, transaccion);
                    MovimientosData.Instancia.AgregarConFecha(movimiento, transaccion);

                    comandoAlmacenamiento = BaseDatos.GetStoredProcCommand("SelectAlmacenamientos");
                    BaseDatos.AddInParameter(comandoAlmacenamiento, "loteID", DbType.Int32, trasladoAntiguo.loteID);
                    BaseDatos.AddInParameter(comandoAlmacenamiento, "almacenID", DbType.Int16, trasladoAntiguo.destinoAlmacenID);

                    using (var lector = BaseDatos.ExecuteReader(comandoAlmacenamiento, transaccion))
                    {
                        if (lector.Read())
                        {
                            stock = MathExpression.Round(lector.GetDouble(lector.GetOrdinal("stock")));
                        }
                    }

                    movimiento.cantidad = -1 * trasladoAntiguo.cantidad;
                    movimiento.saldo = stock + trasladoAntiguo.cantidad;
                    movimiento.almacenID = trasladoAntiguo.destinoAlmacenID;

                    AlmacenamientosData.Instancia.ModificarStock(movimiento.loteID, movimiento.almacenID, movimiento.cantidad, transaccion);
                    LotesData.Instancia.ModificarStock(movimiento.loteID, movimiento.cantidad, transaccion);
                    InsumosData.Instancia.ModificarStock(movimiento.insumoID, movimiento.cantidad, transaccion);
                    MovimientosData.Instancia.AgregarConFecha(movimiento, transaccion);

                    var comando = BaseDatos.GetStoredProcCommand("DeleteTraslado");
                    BaseDatos.AddInParameter(comando, "trasladoID", DbType.Int32, traslado.trasladoID);

                    var resultado = BaseDatos.ExecuteNonQuery(comando, transaccion);
                    if (resultado == 0) throw new Exception("Error al Eliminar Traslado.");

                    transaccion.Commit();
                }
                catch (Exception ex)
                {
                    transaccion.Rollback();
                    throw new Exception(ex.Message);
                }
                conexion.Close();
            }
            return true;
        }

        #endregion
    }
}
